Repository navigation
Listening stats (Home "nice list" ledger), rebased onto main - #7
Merged
Merged
Conversation
…gn branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VriHZgxfrBNm1js1UNLYTN
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VriHZgxfrBNm1js1UNLYTN
Artists only, rolling aggregates, no event log. Every dimension left out can only ever count forward from the day it's added — that trade was made deliberately, along with all-time-only, Home-only, and audio-only. ListeningCredit holds the per-track accumulator and the min(50%, 240s) play threshold, measured against listened time rather than playhead position so scrubbing to the end earns nothing. PlayerService feeds it from the existing 4Hz tick but calls the store only at commit boundaries, so ticking seconds never reach @observable state. LedgerSummary is the pure totals-and-ranking policy; all three compile into AppleNugsTests. The Home ledger is typographic — no cards, no tiles — and gates on hasAnyPlay rather than on the table, so a commit doesn't invalidate the greeting, hero, favorites strip and crate grid along with it. The store refuses to write while a failed load is latched. These tallies have nothing behind them to re-derive from, so a file that didn't decode is salvaged per-element and left on disk rather than overwritten; reset clears the latch, since a deliberate wipe is the intervention it was waiting for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rebasing listening-stats onto main met 8c3c0d7, which takes the 4Hz tick off in the background. Credit was fed only from that tick, so locked-screen listening earned nothing, and the first tick back in front clamped the whole stretch to 1s. ListeningCredit gains span(delta:wallSeconds:duration:): playhead movement since the last sample, capped at the real time that passed, so a scrub forward inside a span still earns at most what elapsed. PlayerService keeps one baseline (position + monotonic uptime) stamped by every tick and every settle, and settles at each commit boundary, on the existing once-a-minute background wake (no new wake, no background tick), before the tick returns on foregrounding, at a track's end, before a gapless manual skip, and before a seek, which then rebases the baseline. A rotation rebases to the resume point rather than 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…at's unverified Carries the old branch's CLAUDE.md notes (the three silent-failure rules, the deliberate scope cuts, the unverified list) into the current file rather than replaying its conflicting Pick-up-here edits, and adds the fourth rule this rebase introduced: backgrounded credit is settled, not ticked. Suite count 307 -> 338. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of the background-credit change found that settleListening trusted livePosition, which falls back to the tick-fed currentTime whenever the player's head isn't our item. Stale in the background, that pulled the baseline down and let the next settle re-credit the gap. At a queue's end this credited the closing track a second time (up to its full length) and could mint a play; a wake landing inside a gapless hand-off lost a span. Settles now read creditPosition, which is nil rather than stale when the head doesn't match, and a nil sample leaves the baseline alone. Paused to playing (our own resume/toggle and external flips seen by the KVO) re-anchors the baseline without crediting, so a mid-seek settle can't carry a skipped distance across a paused span; an external pause settles. adoptPreload clears an unapplied resume offset before rotating, and a restored queue anchors at its resume point. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seek(by:) added the delta to the tick-fed currentTime, which has been stale in the background since 8c3c0d7 took the tick off there. Ten minutes after locking, the lock screen's +30s landed about 9.5 minutes back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebases the Aug-2026
listening-statsbranch ontomain(e6ab835) so the Yeti redesign's Home ships with the real ledger. Lands by fast-forward, as with #5 and #6.Commits
feat: keep all-time listening stats: Tim's original feature commit, replayed. The conflicts were all additive (project.yml test sources, IOSRootView menu/state, PlayerService). Main had replacedpersistNow()withappDidEnterBackground(), so backgrounding now commits listening there.fix: credit listening while the iOS app is backgrounded: main's8c3c0d7takes the 4 Hz tick off in the background, and credit was fed only from that tick, so locked-screen listening earned nothing. Adds the pureListeningCredit.spanrule: the playhead delta, capped at wall-clock time since the last sample.PlayerServicesettles at commit boundaries and on the existing 60 s background wake. No new wake and no background tick.fix: settle listening only from a readable playhead; re-anchor on resume: findings from an independent review. A settle could fall back to the stalecurrentTime, which re-credited a closing track at a background queue end and could mint a play. Paused→playing now re-anchors the baseline, and a restored or adopted track anchors correctly.fix: lock-screen skip seeks from the live playhead: an adjacent bug from the same tick removal.seek(by:)used the stalecurrentTime.Gate (local, Xcode 27.2, fresh DerivedData): both schemes
BUILD SUCCEEDEDwarning-free under strict concurrency.AppleNugsTests338 tests, 0 failures (307 on main + 25 from the feature + 6 new span tests).Not verified on device: the populated ledger, locked-screen credit on the Duo, and the iOS reset dialog. CLAUDE.md lists these.
🤖 Generated with Claude Code